package com.mapforge.gui;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import javax.swing.JCheckBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import com.mapforge.Constants;
import com.mapforge.listener.TileLayerEditEvent;
import com.mapforge.listener.TileLayerEditListener;
import com.mapforge.map.TileLayer;
import com.mapforge.map.tools.undo.tilelayeredit.TileLayerLockEdit;
import com.mapforge.map.tools.undo.tilelayeredit.TileLayerNameEdit;
import com.mapforge.map.tools.undo.tilelayeredit.TileLayerVisibilityEdit;

/**
 * This class displays information, and allows editing of details about a Tile
 * Layer such as the layer's name, its visibility, whether it allows tile
 * writing, and its z-depth.
 * 
 * @author Will Morrison
 * 
 */
public class TileLayerTransferablePanel extends TransferablePanel {
	/**
	 * Default serial version ID, here to remove warnings.
	 */
	private static final long serialVersionUID = 1L;
	private transient TileLayer tileLayer;
	private JTextFieldLabel txtNameLbl;
	private JCheckBox chkVisible;
	private JCheckBox chkLocked;

	private transient List<TileLayerEditListener> tileLayerChangeListeners;
	private static final Dimension PREFERRED_SIZE = new Dimension(200, 100);
	private static JPopupMenuProvider<TileLayerTransferablePanel> popupProvider;

	public TileLayerTransferablePanel(TileLayer tileLayer) {
		super();
		setPreferredSize(PREFERRED_SIZE);
		setMaximumSize(new Dimension(9999, 50));
		this.tileLayer = tileLayer;

		tileLayerChangeListeners = new ArrayList<TileLayerEditListener>();

		TileLayerListener listener = new TileLayerListener();
		addMouseListener(listener);

		txtNameLbl = new JTextFieldLabel();
		chkVisible = new JCheckBox("", Constants.EYE_CLOSED_ICON);
		chkVisible.setSelectedIcon(Constants.EYE_OPEN_ICON);
		chkVisible.setOpaque(false);
		chkLocked = new JCheckBox("", Constants.PADLOCK_UNLOCKED_ICON);
		chkLocked.setSelectedIcon(Constants.PADLOCK_LOCKED_ICON);
		chkLocked.setOpaque(false);

		chkVisible.setSelected(tileLayer.isVisible());
		chkLocked.setSelected(tileLayer.isLocked());

		txtNameLbl.setText(tileLayer.getName());
		txtNameLbl.addActionListener(listener);
		chkVisible.addActionListener(listener);
		chkLocked.addActionListener(listener);

		JPanel chkContainer = new JPanel(new FlowLayout(FlowLayout.LEFT, 1, 1));
		   chkContainer.setFocusable(true);
		this.setFocusable(true);
		chkContainer.setOpaque(false);
		chkContainer.add(txtNameLbl);
		chkContainer.add(chkVisible);
		chkContainer.add(chkLocked);
		this.add(chkContainer);
	}

	@Override
	public void addKeyListener(KeyListener listener) {
		super.addKeyListener(listener);
		Stack<Component[]> stack = new Stack<Component[]>();
		stack.add(this.getComponents());
		Component[] children;
		while (!stack.isEmpty()) {
			children = stack.pop();
			for (int i = 0; i < children.length; i++) {
				children[i].addKeyListener(listener);
				System.out.println("Add key listener to " + children[i]);
				if (children[i] instanceof Container) {
					Container container = (Container) children[i];
					stack.add(container.getComponents());
				}
			}
		}
	}

	@Override
	public Component add(Component c) {
		KeyListener[] listeners = this.getKeyListeners();
		// add key listeners to passed component
		for (int j = 0; j < listeners.length; j++) {
			c.addKeyListener(listeners[j]);
		}

		// add key listeners to all components children if the component has
		// children
		if (c instanceof Container) {
			Container container = (Container) c;
			Component child;
			Stack<Component[]> stack = new Stack<Component[]>();
			stack.add(container.getComponents());
			while (!stack.isEmpty()) {
				Component[] children = stack.pop();
				for (int i = 0; i < children.length; i++) {
					child = children[i];
					for (int j = 0; j < listeners.length; j++) {
						child.addKeyListener(listeners[j]);
					}
					if (child instanceof Container) {
						stack.add(((Container) child).getComponents());
					}
				}
			}
		}
		return super.add(c);
	}

	protected void fireTileLayerEditEvent(TileLayerEditEvent event) {
		for (TileLayerEditListener listener : tileLayerChangeListeners) {
			listener.tileLayerEditPerformed(event);
		}
	}

	public void addTileLayerEditListener(TileLayerEditListener listener) {
		tileLayerChangeListeners.add(listener);
	}

	public boolean removeTileLayerChangeListener(TileLayerEditListener listener) {
		return tileLayerChangeListeners.remove(listener);
	}

	/**
	 * Returns the tile layer this panel is displaying
	 * 
	 * @return tile layer panel displays
	 */
	public TileLayer getTileLayer() {
		return tileLayer;
	}

	/**
	 * 
	 * @param layer
	 */
	public void setTileLayer(TileLayer tileLayer) {
		this.tileLayer = tileLayer;
	}

	private class TileLayerListener extends MouseAdapter implements
			ActionListener {
		private void checkShowPopup(MouseEvent e) {
			if (e.isPopupTrigger()) {
				popupProvider.showJPopupMenu(e);
			}
		}

		public void mouseClicked(MouseEvent e) {
			checkShowPopup(e);
		}

		public void mousePressed(MouseEvent e) {
			checkShowPopup(e);
		}

		public void mouseReleased(MouseEvent e) {
			checkShowPopup(e);
		}

		public void actionPerformed(ActionEvent e) {
			Object obj = e.getSource();
			if (obj == txtNameLbl) {
				String txt = txtNameLbl.getText();
				if (txt.equals("")) {
					JOptionPane.showMessageDialog(
							TileLayerTransferablePanel.this, "Tilelayer "
									+ tileLayer.getName()
									+ " must have a name.");
					txtNameLbl.setText(tileLayer.getName());
				} else {
					String name = tileLayer.getName();
					tileLayer.setName(txt);
					TileLayerEditEvent evt = new TileLayerEditEvent(
							TileLayerTransferablePanel.this, tileLayer,
							new TileLayerNameEdit(tileLayer, name, tileLayer
									.getName()));
					fireTileLayerEditEvent(evt);
				}
			} else if (obj == chkVisible) {
				tileLayer.setVisible(!tileLayer.isVisible());
				TileLayerEditEvent evt = new TileLayerEditEvent(
						TileLayerTransferablePanel.this, tileLayer,
						new TileLayerVisibilityEdit(tileLayer));
				fireTileLayerEditEvent(evt);
			} else if (obj == chkLocked) {
				tileLayer.setLocked(!tileLayer.isLocked());
				TileLayerEditEvent evt = new TileLayerEditEvent(
						TileLayerTransferablePanel.this, tileLayer,
						new TileLayerLockEdit(tileLayer));
				fireTileLayerEditEvent(evt);
			}
		}
	}
}